// 视频转换的入口

import path from 'node:path';
import { fileURLToPath } from 'url';
import { fork } from 'node:child_process';
import { read, writeError, fixError } from './jsonData.js';

import { setFileList, rename, readdir, stat, removeFileList, throttle, ysgc, isEmpty } from '../common.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// import

// 输入路径
const inputDir = String.raw`E:\迅雷下载\待处理剧集\苍兰诀`;

// 输出路径
const outputDir = inputDir;
//const outputDir = 'E:\\火力2x2';

// 合成视频的并发线程数
const MAX_CONCURRENT_THREADS = 4;

// ANCI颜色值
const colorReset = '\x1b[0m';
const colors = ['\x1b[31m', '\x1b[32m', '\x1b[33m', '\x1b[34m', '\x1b[35m', '\x1b[36m', '\x1b[37m'];

// 改名后的文件是否可用
const validFiles = arr => {
    const filelistName = setFileList(arr, 6);
    const child = fork(path.join(__dirname, 'jxv.js'));
    child.send(filelistName);

    // 监听 child 的返回结果
    child.on('message', event => {
        const v = JSON.parse(event);
        if (v.length === 0) {
            console.log('检查完成,无误');
        } else {
            console.log('检查完成，这些可能损坏===>', v);
            v.forEach(item => {
                writeError({
                    [item]: '视频可能损坏'
                });
            });
        }

        child.kill('SIGINT');
    });

    // 监听 child 的错误
    child.on('error', error => {
        console.log(`检查完成error`);
        child.kill('SIGINT');
    });
};

// 取打印的颜色值，需要排除传进来的
const getColors = arr => {
    let colorIndex = '';
    while (arr.includes(colorIndex) || !colorIndex) {
        colorIndex = Math.floor(Math.random() * colors.length);
    }
    return colorIndex;
};

//  每一个的目录、文件名、目的地址 dirPath, tsVideoArr, nameX
const joinTS = trans => {
    // 当前正在运行的线程数
    let activeThreads = 0;

    // 任务队列
    const taskQueue = [...trans];

    // 处理输出进度的函数。有删除、添加两种状态，添加时输出序列，给删除用
    const logArr = [];
    // 固定它的长度
    logArr.length = MAX_CONCURRENT_THREADS;
    const logMessage = z => {
        if (typeof z === 'object' && !Object.hasOwn(z, 'index')) {
            const index = logArr.findIndex(item => !item);
            const colorIndex = getColors(logArr.filter(item => item).map(item => item.colorIndex));
            logArr[index] = { ...z, colorIndex };
            return index;
        } else if (typeof z === 'object' && Object.hasOwn(z, 'index')) {
            const index = z.index;
            logArr[index] = { ...logArr[index], ...z };
            return index;
        } else if (!isEmpty(z)) {
            logArr[z] = null;
        }
    };

    // 输出进度
    const log = () => {
        const strArr = logArr
            .filter(item => item)
            .map(item => {
                if (item.type === 'progressT') {
                    const endNum = 100;
                    const timemarkNum = Math.floor((item.i / item.len) * endNum);
                    return `${colors[item.colorIndex]}${item.nameW}-计时进度${'>'.repeat(timemarkNum)}${'.'.repeat(endNum - timemarkNum)}${
                        Math.floor((timemarkNum / endNum) * 10000) / 100
                    }%${colorReset}`;
                } else {
                    const endNum = 100;
                    const timemarkNum = Math.floor((item.timemark / item.timeend) * endNum);
                    return `${colors[item.colorIndex]}${item.nameW}-转换进度${'>'.repeat(timemarkNum)}${'.'.repeat(endNum - timemarkNum)}${
                        Math.floor((timemarkNum / endNum) * 10000) / 100
                    }%${colorReset}`;
                }
            });

        // 清屏
        console.clear();
        strArr.forEach(item => console.log(item));
        // console.log(`当前有${strArr.length}个线程正在处理视频合成任务`);
    };
    const tlod = throttle(log, 400);

    // 处理任务的函数
    const processTask = task => {
        return new Promise(resolve => {
            // 创建一个 child 线程
            const child = fork(path.join(__dirname, 'jxc.js'), {
                execArgv: ['--inspect=9230'] // 使用不同的端口以避免冲突(调试用)
            });

            // 向 child 发送任务数据
            child.send(JSON.stringify(task));
            console.log(`${task.nameW} 任务开始`);
            let index;

            // 监听 child 的返回结果
            child.on('message', str => {
                const obj = JSON.parse(str);
                if (obj.type === 'start') {
                    console.log(`${task.nameW} 转换开始:`, obj.nameW);
                    if (!isEmpty(index)) {
                        logMessage({ ...obj, index });
                    } else {
                        index = logMessage(obj);
                    }
                } else if (obj.type === 'end') {
                    console.log(`${task.nameW} 转换成功:`, obj.nameW);
                    // 取消数组（归null）
                    fixError(obj.nameW);
                    logMessage(index);
                    child.kill('SIGINT'); // 关闭 child 进程
                    resolve(index); // 任务完成，标记为 resolved
                } else if (obj.type === 'error') {
                    // console.log(`${task.nameW} 错误`, obj.nameW);
                    writeError({
                        [task.nameW]: `计时失败：${obj.dirEnt}`
                    });

                    logMessage(index);
                    child.kill('SIGINT'); // 关闭 child 进程
                    resolve(index); // 任务完成，标记为 resolved
                } else if (obj.type === 'progress' || obj.type === 'progressT') {
                    // 过程
                    if (!isEmpty(index)) {
                        logMessage({ ...obj, index });
                    } else {
                        index = logMessage(obj);
                    }
                    tlod();
                } else {
                    writeError({
                        [task.nameW]: '任务出错2'
                    });
                }
            });

            // 监听 child 的错误
            child.on('error', error => {
                writeError({
                    [task.nameW]: '线程本身出错'
                });

                console.error(`线程本身出错 ${task.nameX}`);

                child.kill('SIGINT'); // 关闭 child 进程

                resolve(); // 即使出错，也要标记为 resolved
            });
        });
    };

    // 启动任务调度
    const startTaskScheduler = async () => {
        while (activeThreads < MAX_CONCURRENT_THREADS && taskQueue.length > 0) {
            const { dirPath, tsVideoArr, nameW, nameX } = taskQueue.shift(); // 从队列中取出一个任务
            activeThreads++; // 增加正在运行的线程数

            const inputFiles = tsVideoArr.map(item => path.join(dirPath, item));

            const filelistName = setFileList(inputFiles, 10, nameW);

            processTask({ filelistName, nameW, nameX }).then(index => {
                // 删除文件
                removeFileList(filelistName);
                activeThreads--; // 任务完成，减少正在运行的线程数
                startTaskScheduler(); // 检查是否有更多任务需要处理
            });
        }
    };

    // 启动任务调度
    startTaskScheduler();
};

const doInit = async () => {
    const res = await read(true);
    const ent = Object.entries(res);
    const files = await readdir(inputDir);
    // 需要合并视频的情况
    const trans = [];

    const renameArr = [];

    let count = 0;

    files.forEach(async (file, index) => {
        const filePath = path.join(inputDir, file);
        const status = await stat(filePath);
        // 其它情况或者-m3u8的文件夹（影视工场）

        if (status === 1) {
            const ysgcDirName = filePath + '-m3u8';

            const enti = ent.find(item => file.includes(item[0]));

            const num = enti ? enti[1] : '';
            if (!num) {
                console.log(`json文件中没有对应的此文件的记录，不处理--->出错`);
                count++;
            } else {
                // 目的名
                const nameW = num + '.mp4';
                const nameX = path.join(outputDir, nameW);

                if ((await stat(ysgcDirName)) === 2) {
                    // 如果有一个文件夹跟这个文件有同步的情况，正式是影视工场的情况，开始处理影视工场的情况
                    const tsVideoArr = await ysgc(filePath);
                    trans.push({ dirPath: ysgcDirName, tsVideoArr, nameX, nameW });
                    count++;
                } else if (file.endsWith('.video')) {
                    // 这是速搜单文件的情况
                    console.log(`${filePath} 是一个文件,带video的直接改名`);

                    //直接改名
                    rename(filePath, nameX);
                    renameArr.push(nameX);
                    count++;
                } else {
                    console.log(`${file} 是一个文件,不带video，没有处理`);
                    count++;
                }
            }
        } else if (status === 2 && !file.endsWith('-m3u8')) {
            // endsWith('-m3u8'):不处理影视工场的情况
            // 这里分两种情况，一种是里面直接有.video的情况，把他找出来，改名至目录
            // 一种是拼接ts
            const enti = ent.find(item => item[0] === file);
            if (!enti) {
                console.log(`json文件中没有对应的，不处理`);
                count++;
                return;
            } else {
                const num = enti[1];

                const dirPath = path.join(inputDir, file);
                const filess = await readdir(dirPath);
                if (filess === 'err') {
                    return;
                }

                const findVideo = filess.find(item => item.lastIndexOf('.video') > -1);

                const tsVideoArr = filess
                    .filter(item => item.lastIndexOf('.ts') > -1 || item.indexOf('video_') > -1)
                    // 对视频排序（仅针对速搜视频的情况，影视工场另有顺序）
                    .sort((a1, a2) => {
                        const a1d = /\d+/.exec(a1);
                        const a2d = /\d+/.exec(a2);
                        if ((a1d, a2d)) {
                            return JSON.parse(a1d[0]) - JSON.parse(a2d[0]);
                        } else {
                            return -1;
                        }
                    });

                // 目的地
                const nameW = num + '.mp4';
                const nameX = path.join(outputDir, nameW);

                if (findVideo) {
                    // 直接改名
                    const oldPathName = path.join(dirPath, findVideo);
                    rename(oldPathName, nameX);
                    renameArr.push(nameX);
                } else if (tsVideoArr.length > 0) {
                    // 每一个的目录、文件名、目的地址
                    trans.push({ dirPath, tsVideoArr, nameX, nameW });
                } else {
                    console.log(`${filePath} 文件夹不符合情况的不处理`);
                }
                count++;
            }
        } else {
            count++;
        }

        if (count === files.length) {
            if (renameArr.length > 0) {
                console.log('检查改名的视频文件是否损坏');
                validFiles(renameArr);
            }
            // console.log(trans.length);
            if (trans.length > 0) {
                console.log('ts拼接');
                joinTS(trans);
            }
        }
    });
};

doInit();
